Passed
Pull Request — master (#29)
by
unknown
03:17
created

WaveFileReader.readUInt16_   A

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
/*
2
 * Copyright (c) 2017-2019 Rafael da Silva Rocha.
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining
5
 * a copy of this software and associated documentation files (the
6
 * "Software"), to deal in the Software without restriction, including
7
 * without limitation the rights to use, copy, modify, merge, publish,
8
 * distribute, sublicense, and/or sell copies of the Software, and to
9
 * permit persons to whom the Software is furnished to do so, subject to
10
 * the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be
13
 * included in all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
 *
23
 */
24
25
/**
26
 * @fileoverview The WaveFileReader class.
27
 * @see https://github.com/rochars/wavefile
28
 */
29
30
import { RIFFFile } from "./riff-file";
31
import { unpackString, unpack } from "./parsers/binary";
32
33
/**
34
 * A class to read wav files.
35
 * @extends RIFFFile
36
 */
37
export class WaveFileReader extends RIFFFile {
38
  constructor() {
39
    super();
40
    // Include 'RF64' as a supported container format
41
    this.supported_containers.push("RF64");
42
    /**
43
     * The data of the 'fmt' chunk.
44
     * @type {!Object<string, *>}
45
     */
46
    this.fmt = {
47
      /** @type {string} */
48
      chunkId: "",
49
      /** @type {number} */
50
      chunkSize: 0,
51
      /** @type {number} */
52
      audioFormat: 0,
53
      /** @type {number} */
54
      numChannels: 0,
55
      /** @type {number} */
56
      sampleRate: 0,
57
      /** @type {number} */
58
      byteRate: 0,
59
      /** @type {number} */
60
      blockAlign: 0,
61
      /** @type {number} */
62
      bitsPerSample: 0,
63
      /** @type {number} */
64
      cbSize: 0,
65
      /** @type {number} */
66
      validBitsPerSample: 0,
67
      /** @type {number} */
68
      dwChannelMask: 0,
69
      /**
70
       * 4 32-bit values representing a 128-bit ID
71
       * @type {!Array<number>}
72
       */
73
      subformat: [],
74
      /**
75
       * MPEG additional format information
76
       * when audioFormat == 80
77
       * https://tech.ebu.ch/docs/tech/tech3285s1.pdf
78
       */
79
      /** @type {number} */
80
      headLayer: 0,
81
      /** @type {number} */
82
      headBitRate: 0,
83
      /** @type {number} */
84
      headMode: 0,
85
      /** @type {number} */
86
      headModeExt: 0,
87
      /** @type {number} */
88
      headEmphasis: 0,
89
      /** @type {number} */
90
      headFlags: 0,
91
      /** @type {number} */
92
      ptsLow: 0,
93
      /** @type {number} */
94
      ptsHigh: 0
95
    };
96
    /**
97
     * The data of the 'fact' chunk.
98
     * @type {!Object<string, *>}
99
     */
100
    this.fact = {
101
      /** @type {string} */
102
      chunkId: "",
103
      /** @type {number} */
104
      chunkSize: 0,
105
      /** @type {number} */
106
      dwSampleLength: 0
107
    };
108
    /**
109
     * The data of the 'cue ' chunk.
110
     * @type {!Object<string, *>}
111
     */
112
    this.cue = {
113
      /** @type {string} */
114
      chunkId: "",
115
      /** @type {number} */
116
      chunkSize: 0,
117
      /** @type {number} */
118
      dwCuePoints: 0,
119
      /** @type {!Array<!Object>} */
120
      points: []
121
    };
122
    /**
123
     * The data of the 'smpl' chunk.
124
     * @type {!Object<string, *>}
125
     */
126
    this.smpl = {
127
      /** @type {string} */
128
      chunkId: "",
129
      /** @type {number} */
130
      chunkSize: 0,
131
      /** @type {number} */
132
      dwManufacturer: 0,
133
      /** @type {number} */
134
      dwProduct: 0,
135
      /** @type {number} */
136
      dwSamplePeriod: 0,
137
      /** @type {number} */
138
      dwMIDIUnityNote: 0,
139
      /** @type {number} */
140
      dwMIDIPitchFraction: 0,
141
      /** @type {number} */
142
      dwSMPTEFormat: 0,
143
      /** @type {number} */
144
      dwSMPTEOffset: 0,
145
      /** @type {number} */
146
      dwNumSampleLoops: 0,
147
      /** @type {number} */
148
      dwSamplerData: 0,
149
      /** @type {!Array<!Object>} */
150
      loops: []
151
    };
152
    /**
153
     * The data of the 'bext' chunk.
154
     * @type {!Object<string, *>}
155
     */
156
    this.bext = {
157
      /** @type {string} */
158
      chunkId: "",
159
      /** @type {number} */
160
      chunkSize: 0,
161
      /** @type {string} */
162
      description: "", //256
163
      /** @type {string} */
164
      originator: "", //32
165
      /** @type {string} */
166
      originatorReference: "", //32
167
      /** @type {string} */
168
      originationDate: "", //10
169
      /** @type {string} */
170
      originationTime: "", //8
171
      /**
172
       * 2 32-bit values, timeReference high and low
173
       * @type {!Array<number>}
174
       */
175
      timeReference: [0, 0],
176
      /** @type {number} */
177
      version: 0, //WORD
178
      /** @type {string} */
179
      UMID: "", // 64 chars
180
      /** @type {number} */
181
      loudnessValue: 0, //WORD
182
      /** @type {number} */
183
      loudnessRange: 0, //WORD
184
      /** @type {number} */
185
      maxTruePeakLevel: 0, //WORD
186
      /** @type {number} */
187
      maxMomentaryLoudness: 0, //WORD
188
      /** @type {number} */
189
      maxShortTermLoudness: 0, //WORD
190
      /** @type {string} */
191
      reserved: "", //180
192
      /** @type {string} */
193
      codingHistory: "" // string, unlimited
194
    };
195
    /**
196
     * The data of the 'mext' chunk.
197
     * @type {!Object<string, *>}
198
     */
199
    this.mext = {
200
      /** @type {string} */
201
      chunkId: "",
202
      /** @type {number} */
203
      chunkSize: 0,
204
      /** @type {number} */
205
      soundInformation: 0,
206
      /** @type {number} */
207
      frameSize: 0,
208
      /** @type {number} */
209
      ancillaryDataLength: 0,
210
      /** @type {number} */
211
      ancillaryDataDef: 0, //4
212
      /** @type {string} */
213
      reserved: ""
214
    };
215
    /**
216
     * The cart of the cart chunk.
217
     * @type {!Object<string, *>}
218
     */
219
    this.cart = {
220
      /** @type {string} */
221
      chunkId: "",
222
      /** @type {number} */
223
      chunkSize: 0,
224
      /** @type {string} */
225
      version: "",
226
      /** @type {string} */
227
      title: "",
228
      /** @type {string} */
229
      artist: "",
230
      /** @type {string} */
231
      cutId: "",
232
      /** @type {string} */
233
      clientId: "",
234
      /** @type {string} */
235
      category: "",
236
      /** @type {string} */
237
      classification: "",
238
      /** @type {string} */
239
      outCue: "",
240
      /** @type {string} */
241
      startDate: "",
242
      /** @type {string} */
243
      startTime: "",
244
      /** @type {string} */
245
      endDate: "",
246
      /** @type {string} */
247
      endTime: "",
248
      /** @type {string} */
249
      producerAppId: "",
250
      /** @type {string} */
251
      producerAppVersion: "",
252
      /** @type {string} */
253
      userDef: "",
254
      /** @type {number} */
255
      levelReference: 0,
256
      /** @type {!Array<!Object>} */
257
      postTimer: [],
258
      /** @type {string} */
259
      reserved: "",
260
      /** @type {string} */
261
      url: "",
262
      /** @type {string} */
263
      tagText: ""
264
    };
265
    /**
266
     * The data of the 'iXML' chunk.
267
     * @type {!Object<string, *>}
268
     */
269
    this.iXML = {
270
      /** @type {string} */
271
      chunkId: "",
272
      /** @type {number} */
273
      chunkSize: 0,
274
      /** @type {string} */
275
      value: ""
276
    };
277
    /**
278
     * The data of the 'ds64' chunk.
279
     * Used only with RF64 files.
280
     * @type {!Object<string, *>}
281
     */
282
    this.ds64 = {
283
      /** @type {string} */
284
      chunkId: "",
285
      /** @type {number} */
286
      chunkSize: 0,
287
      /** @type {number} */
288
      riffSizeHigh: 0, // DWORD
289
      /** @type {number} */
290
      riffSizeLow: 0, // DWORD
291
      /** @type {number} */
292
      dataSizeHigh: 0, // DWORD
293
      /** @type {number} */
294
      dataSizeLow: 0, // DWORD
295
      /** @type {number} */
296
      originationTime: 0, // DWORD
297
      /** @type {number} */
298
      sampleCountHigh: 0, // DWORD
299
      /** @type {number} */
300
      sampleCountLow: 0 // DWORD
301
      /** @type {number} */
302
      //'tableLength': 0, // DWORD
303
      /** @type {!Array<number>} */
304
      //'table': []
305
    };
306
    /**
307
     * The data of the 'data' chunk.
308
     * @type {!Object<string, *>}
309
     */
310
    this.data = {
311
      /** @type {string} */
312
      chunkId: "",
313
      /** @type {number} */
314
      chunkSize: 0,
315
      /** @type {!Uint8Array} */
316
      samples: new Uint8Array(0)
317
    };
318
    /**
319
     * The data of the 'LIST' chunks.
320
     * Each item in this list look like this:
321
     *  {
322
     *      chunkId: '',
323
     *      chunkSize: 0,
324
     *      format: '',
325
     *      subChunks: []
326
     *   }
327
     * @type {!Array<!Object>}
328
     */
329
    this.LIST = [];
330
    /**
331
     * The data of the 'junk' chunk.
332
     * @type {!Object<string, *>}
333
     */
334
    this.junk = {
335
      /** @type {string} */
336
      chunkId: "",
337
      /** @type {number} */
338
      chunkSize: 0,
339
      /** @type {!Array<number>} */
340
      chunkData: []
341
    };
342
    /**
343
     * The data of the '_PMX' chunk.
344
     * @type {!Object<string, *>}
345
     */
346
    this._PMX = {
347
      /** @type {string} */
348
      chunkId: "",
349
      /** @type {number} */
350
      chunkSize: 0,
351
      /** @type {string} */
352
      value: ""
353
    };
354
    /**
355
     * @type {{be: boolean, bits: number, fp: boolean, signed: boolean}}
356
     * @protected
357
     */
358
    this.uInt16 = { bits: 16, be: false, signed: false, fp: false };
359
  }
360
361
  /**
362
   * Set up the WaveFileReader object from a wav byte buffer.
363
   * @param {!Uint8Array} wavBuffer The buffer.
364
   * @param {boolean=} [samples=true] True if the samples should be loaded.
365
   * @throws {Error} If container is not RIFF, RIFX or RF64.
366
   * @throws {Error} If format is not WAVE.
367
   * @throws {Error} If no 'fmt ' chunk is found.
368
   * @throws {Error} If no 'data' chunk is found.
369
   */
370
  fromBuffer(wavBuffer, samples = true) {
371
    // Always should reset the chunks when reading from a buffer
372
    this.clearHeaders();
373
    this.setSignature(wavBuffer);
374
    this.uInt16.be = this.uInt32.be;
375
    if (this.format != "WAVE") {
376
      throw Error('Could not find the "WAVE" format identifier');
377
    }
378
    this.readDs64Chunk_(wavBuffer);
379
    this.readFmtChunk_(wavBuffer);
380
    this.readFactChunk_(wavBuffer);
381
    this.readBextChunk_(wavBuffer);
382
    this.readMextChunk_(wavBuffer);
383
    this.readCartChunk_(wavBuffer);
384
    this.readiXMLChunk_(wavBuffer);
385
    this.readCueChunk_(wavBuffer);
386
    this.readSmplChunk_(wavBuffer);
387
    this.readDataChunk_(wavBuffer, samples);
388
    this.readJunkChunk_(wavBuffer);
389
    this.readLISTChunk_(wavBuffer);
390
    this.read_PMXChunk_(wavBuffer);
391
  }
392
393
  /**
394
   * Reset the chunks of the WaveFileReader instance.
395
   * @protected
396
   * @ignore
397
   */
398
  clearHeaders() {
399
    /** @type {!Object} */
400
    let tmpWav = new WaveFileReader();
401
    Object.assign(this.fmt, tmpWav.fmt);
402
    Object.assign(this.fact, tmpWav.fact);
403
    Object.assign(this.cue, tmpWav.cue);
404
    Object.assign(this.smpl, tmpWav.smpl);
405
    Object.assign(this.bext, tmpWav.bext);
406
    Object.assign(this.mext, tmpWav.mext);
407
    Object.assign(this.cart, tmpWav.cart);
408
    Object.assign(this.iXML, tmpWav.iXML);
409
    Object.assign(this.ds64, tmpWav.ds64);
410
    Object.assign(this.data, tmpWav.data);
411
    this.LIST = [];
412
    Object.assign(this.junk, tmpWav.junk);
413
    Object.assign(this._PMX, tmpWav._PMX);
414
  }
415
416
  /**
417
   * Read the 'fmt ' chunk of a wave file.
418
   * @param {!Uint8Array} buffer The wav file buffer.
419
   * @throws {Error} If no 'fmt ' chunk is found.
420
   * @private
421
   */
422
  readFmtChunk_(buffer) {
423
    /** @type {?Object} */
424
    let chunk = this.findChunk("fmt ");
425
    if (chunk) {
426
      this.head = chunk.chunkData.start;
427
      this.fmt.chunkId = chunk.chunkId;
428
      this.fmt.chunkSize = chunk.chunkSize;
429
      this.fmt.audioFormat = this.readUInt16_(buffer);
430
      this.fmt.numChannels = this.readUInt16_(buffer);
431
      this.fmt.sampleRate = this.readUInt32(buffer);
432
      this.fmt.byteRate = this.readUInt32(buffer);
433
      this.fmt.blockAlign = this.readUInt16_(buffer);
434
      this.fmt.bitsPerSample = this.readUInt16_(buffer);
435
      this.readFmtExtension_(buffer);
436
    } else {
437
      throw Error('Could not find the "fmt " chunk');
438
    }
439
  }
440
441
  /**
442
   * Read the 'fmt ' chunk extension.
443
   * @param {!Uint8Array} buffer The wav file buffer.
444
   * @private
445
   */
446
  readFmtExtension_(buffer) {
447
    if (this.fmt.chunkSize > 16) {
448
      this.fmt.cbSize = this.readUInt16_(buffer);
449
      if (this.fmt.audioFormat == 80 && this.fmt.chunkSize == 40) {
450
        this.fmt.headLayer = this.readUInt16_(buffer);
451
        this.fmt.headBitRate = this.readUInt32(buffer);
452
        this.fmt.headMode = this.readUInt16_(buffer);
453
        this.fmt.headModeExt = this.readUInt16_(buffer);
454
        this.fmt.headEmphasis = this.readUInt16_(buffer);
455
        this.fmt.headFlags = this.readUInt16_(buffer);
456
        this.fmt.ptsLow = this.readUInt32(buffer);
457
        this.fmt.ptsHigh = this.readUInt32(buffer);
458
      } else if (this.fmt.chunkSize > 18) {
459
        this.fmt.validBitsPerSample = this.readUInt16_(buffer);
460
        if (this.fmt.chunkSize > 20) {
461
          this.fmt.dwChannelMask = this.readUInt32(buffer);
462
          this.fmt.subformat = [
463
            this.readUInt32(buffer),
464
            this.readUInt32(buffer),
465
            this.readUInt32(buffer),
466
            this.readUInt32(buffer)
467
          ];
468
        }
469
      }
470
    }
471
  }
472
473
  /**
474
   * Read the 'fact' chunk of a wav file.
475
   * @param {!Uint8Array} buffer The wav file buffer.
476
   * @private
477
   */
478
  readFactChunk_(buffer) {
479
    /** @type {?Object} */
480
    let chunk = this.findChunk("fact");
481
    if (chunk) {
482
      this.head = chunk.chunkData.start;
483
      this.fact.chunkId = chunk.chunkId;
484
      this.fact.chunkSize = chunk.chunkSize;
485
      this.fact.dwSampleLength = this.readUInt32(buffer);
486
    }
487
  }
488
489
  /**
490
   * Read the 'cue ' chunk of a wave file.
491
   * @param {!Uint8Array} buffer The wav file buffer.
492
   * @private
493
   */
494
  readCueChunk_(buffer) {
495
    /** @type {?Object} */
496
    let chunk = this.findChunk("cue ");
497
    if (chunk) {
498
      this.head = chunk.chunkData.start;
499
      this.cue.chunkId = chunk.chunkId;
500
      this.cue.chunkSize = chunk.chunkSize;
501
      this.cue.dwCuePoints = this.readUInt32(buffer);
502
      for (let i = 0; i < this.cue.dwCuePoints; i++) {
503
        this.cue.points.push({
504
          dwName: this.readUInt32(buffer),
505
          dwPosition: this.readUInt32(buffer),
506
          fccChunk: this.readString(buffer, 4),
507
          dwChunkStart: this.readUInt32(buffer),
508
          dwBlockStart: this.readUInt32(buffer),
509
          dwSampleOffset: this.readUInt32(buffer)
510
        });
511
      }
512
    }
513
  }
514
515
  /**
516
   * Read the 'smpl' chunk of a wave file.
517
   * @param {!Uint8Array} buffer The wav file buffer.
518
   * @private
519
   */
520
  readSmplChunk_(buffer) {
521
    /** @type {?Object} */
522
    let chunk = this.findChunk("smpl");
523
    if (chunk) {
524
      this.head = chunk.chunkData.start;
525
      this.smpl.chunkId = chunk.chunkId;
526
      this.smpl.chunkSize = chunk.chunkSize;
527
      this.smpl.dwManufacturer = this.readUInt32(buffer);
528
      this.smpl.dwProduct = this.readUInt32(buffer);
529
      this.smpl.dwSamplePeriod = this.readUInt32(buffer);
530
      this.smpl.dwMIDIUnityNote = this.readUInt32(buffer);
531
      this.smpl.dwMIDIPitchFraction = this.readUInt32(buffer);
532
      this.smpl.dwSMPTEFormat = this.readUInt32(buffer);
533
      this.smpl.dwSMPTEOffset = this.readUInt32(buffer);
534
      this.smpl.dwNumSampleLoops = this.readUInt32(buffer);
535
      this.smpl.dwSamplerData = this.readUInt32(buffer);
536
      for (let i = 0; i < this.smpl.dwNumSampleLoops; i++) {
537
        this.smpl.loops.push({
538
          dwName: this.readUInt32(buffer),
539
          dwType: this.readUInt32(buffer),
540
          dwStart: this.readUInt32(buffer),
541
          dwEnd: this.readUInt32(buffer),
542
          dwFraction: this.readUInt32(buffer),
543
          dwPlayCount: this.readUInt32(buffer)
544
        });
545
      }
546
    }
547
  }
548
549
  /**
550
   * Read the 'data' chunk of a wave file.
551
   * @param {!Uint8Array} buffer The wav file buffer.
552
   * @param {boolean} samples True if the samples should be loaded.
553
   * @throws {Error} If no 'data' chunk is found.
554
   * @private
555
   */
556
  readDataChunk_(buffer, samples) {
557
    /** @type {?Object} */
558
    let chunk = this.findChunk("data");
559
    if (chunk) {
560
      this.data.chunkId = "data";
561
      this.data.chunkSize = chunk.chunkSize;
562
      if (samples) {
563
        this.data.samples = buffer.slice(
564
          chunk.chunkData.start,
565
          chunk.chunkData.end
566
        );
567
      }
568
    } else {
569
      throw Error('Could not find the "data" chunk');
570
    }
571
  }
572
573
  /**
574
   * Read the 'bext' chunk of a wav file.
575
   * @param {!Uint8Array} buffer The wav file buffer.
576
   * @private
577
   */
578
  readBextChunk_(buffer) {
579
    /** @type {?Object} */
580
    let chunk = this.findChunk("bext");
581
    if (chunk) {
582
      this.head = chunk.chunkData.start;
583
      this.bext.chunkId = chunk.chunkId;
584
      this.bext.chunkSize = chunk.chunkSize;
585
      this.bext.description = this.readString(buffer, 256);
586
      this.bext.originator = this.readString(buffer, 32);
587
      this.bext.originatorReference = this.readString(buffer, 32);
588
      this.bext.originationDate = this.readString(buffer, 10);
589
      this.bext.originationTime = this.readString(buffer, 8);
590
      this.bext.timeReference = [
591
        this.readUInt32(buffer),
592
        this.readUInt32(buffer)
593
      ];
594
      this.bext.version = this.readUInt16_(buffer);
595
      this.bext.UMID = this.readString(buffer, 64);
596
      this.bext.loudnessValue = this.readUInt16_(buffer);
597
      this.bext.loudnessRange = this.readUInt16_(buffer);
598
      this.bext.maxTruePeakLevel = this.readUInt16_(buffer);
599
      this.bext.maxMomentaryLoudness = this.readUInt16_(buffer);
600
      this.bext.maxShortTermLoudness = this.readUInt16_(buffer);
601
      this.bext.reserved = this.readString(buffer, 180);
602
      this.bext.codingHistory = this.readString(
603
        buffer,
604
        this.bext.chunkSize - 602
605
      );
606
    }
607
  }
608
609
  /**
610
   * Read the 'mext' chunk of a wav file.
611
   * @param {!Uint8Array} buffer The wav file buffer.
612
   * @private
613
   */
614
  readMextChunk_(buffer) {
615
    /** @type {?Object} */
616
    let chunk = this.findChunk("mext");
617
    if (chunk) {
618
      this.head = chunk.chunkData.start;
619
      this.mext.chunkId = chunk.chunkId;
620
      this.mext.chunkSize = chunk.chunkSize;
621
      this.mext.soundInformation = this.readUInt16_(buffer);
622
      this.mext.frameSize = this.readUInt16_(buffer);
623
      this.mext.ancillaryDataLength = this.readUInt16_(buffer);
624
      this.mext.ancillaryDataDef = this.readUInt16_(buffer);
625
      this.mext.reserved = this.readString(buffer, 4);
626
    }
627
  }
628
629
  /**
630
   * Read the 'cart' chunk of a wav file.
631
   * @param {!Uint8Array} buffer The wav file buffer.
632
   * @private
633
   */
634
  readCartChunk_(buffer) {
635
    /** @type {?Object} */
636
    let chunk = this.findChunk("cart");
637
    if (chunk) {
638
      this.head = chunk.chunkData.start;
639
      this.cart.chunkId = chunk.chunkId;
640
      this.cart.chunkSize = chunk.chunkSize;
641
      this.cart.version = this.readString(buffer, 4);
642
      this.cart.title = this.readString(buffer, 64);
643
      this.cart.artist = this.readString(buffer, 64);
644
      this.cart.cutId = this.readString(buffer, 64);
645
      this.cart.clientId = this.readString(buffer, 64);
646
      this.cart.category = this.readString(buffer, 64);
647
      this.cart.classification = this.readString(buffer, 64);
648
      this.cart.outCue = this.readString(buffer, 64);
649
      this.cart.startDate = this.readString(buffer, 10);
650
      this.cart.startTime = this.readString(buffer, 8);
651
      this.cart.endDate = this.readString(buffer, 10);
652
      this.cart.endTime = this.readString(buffer, 8);
653
      this.cart.producerAppId = this.readString(buffer, 64);
654
      this.cart.producerAppVersion = this.readString(buffer, 64);
655
      this.cart.userDef = this.readString(buffer, 64);
656
      this.cart.levelReference = this.readUInt32(buffer);
657
      this.cart.postTimer = [];
658
      for (let i = 0; i < 8; i++) {
659
        this.cart.postTimer.push({
660
          usage: this.readString(buffer, 4),
661
          value: this.readUInt32(buffer)
662
        });
663
      }
664
      this.cart.reserved = this.readString(buffer, 276);
665
      this.cart.url = this.readString(buffer, 1024);
666
      this.cart.tagText = this.readString(buffer, chunk.chunkSize - 2048);
667
    }
668
  }
669
670
  /**
671
   * Read the 'iXML' chunk of a wav file.
672
   * @param {!Uint8Array} buffer The wav file buffer.
673
   * @private
674
   */
675
  readiXMLChunk_(buffer) {
676
    /** @type {?Object} */
677
    let chunk = this.findChunk("iXML");
678
    if (chunk) {
679
      this.head = chunk.chunkData.start;
680
      this.iXML.chunkId = chunk.chunkId;
681
      this.iXML.chunkSize = chunk.chunkSize;
682
      this.iXML.value = unpackString(
683
        buffer,
684
        this.head,
685
        this.head + this.iXML.chunkSize
686
      );
687
    }
688
  }
689
690
  /**
691
   * Read the 'ds64' chunk of a wave file.
692
   * @param {!Uint8Array} buffer The wav file buffer.
693
   * @throws {Error} If no 'ds64' chunk is found and the file is RF64.
694
   * @private
695
   */
696
  readDs64Chunk_(buffer) {
697
    /** @type {?Object} */
698
    let chunk = this.findChunk("ds64");
699
    if (chunk) {
700
      this.head = chunk.chunkData.start;
701
      this.ds64.chunkId = chunk.chunkId;
702
      this.ds64.chunkSize = chunk.chunkSize;
703
      this.ds64.riffSizeHigh = this.readUInt32(buffer);
704
      this.ds64.riffSizeLow = this.readUInt32(buffer);
705
      this.ds64.dataSizeHigh = this.readUInt32(buffer);
706
      this.ds64.dataSizeLow = this.readUInt32(buffer);
707
      this.ds64.originationTime = this.readUInt32(buffer);
708
      this.ds64.sampleCountHigh = this.readUInt32(buffer);
709
      this.ds64.sampleCountLow = this.readUInt32(buffer);
710
      //if (wav.ds64.chunkSize > 28) {
711
      //  wav.ds64.tableLength = unpack(
712
      //    chunkData.slice(28, 32), uInt32_);
713
      //  wav.ds64.table = chunkData.slice(
714
      //     32, 32 + wav.ds64.tableLength);
715
      //}
716
    } else {
717
      if (this.container == "RF64") {
718
        throw Error('Could not find the "ds64" chunk');
719
      }
720
    }
721
  }
722
723
  /**
724
   * Read the 'LIST' chunks of a wave file.
725
   * @param {!Uint8Array} buffer The wav file buffer.
726
   * @private
727
   */
728
  readLISTChunk_(buffer) {
729
    /** @type {?Object} */
730
    let listChunks = this.findChunk("LIST", true);
731
    if (listChunks !== null) {
732
      for (let j = 0; j < listChunks.length; j++) {
733
        /** @type {!Object} */
734
        let subChunk = listChunks[j];
735
        this.LIST.push({
736
          chunkId: subChunk.chunkId,
737
          chunkSize: subChunk.chunkSize,
738
          format: subChunk.format,
739
          subChunks: []
740
        });
741
        for (let x = 0; x < subChunk.subChunks.length; x++) {
742
          this.readLISTSubChunks_(
743
            subChunk.subChunks[x],
744
            subChunk.format,
745
            buffer
746
          );
747
        }
748
      }
749
    }
750
  }
751
752
  /**
753
   * Read the sub chunks of a 'LIST' chunk.
754
   * @param {!Object} subChunk The 'LIST' subchunks.
755
   * @param {string} format The 'LIST' format, 'adtl' or 'INFO'.
756
   * @param {!Uint8Array} buffer The wav file buffer.
757
   * @private
758
   */
759
  readLISTSubChunks_(subChunk, format, buffer) {
760
    if (format == "adtl") {
761
      if (["labl", "note", "ltxt"].indexOf(subChunk.chunkId) > -1) {
762
        this.readLISTadtlSubChunks_(buffer, subChunk);
763
      }
764
      // RIFF INFO tags like ICRD, ISFT, ICMT
765
    } else if (format == "INFO") {
766
      this.readLISTINFOSubChunks_(buffer, subChunk);
767
    }
768
  }
769
770
  /**
771
   * Read the sub chunks of a 'LIST' chunk of type 'adtl'.
772
   * @param {!Uint8Array} buffer The wav file buffer.
773
   * @param {!Object} subChunk The 'LIST' subchunks.
774
   * @private
775
   */
776
  readLISTadtlSubChunks_(buffer, subChunk) {
777
    this.head = subChunk.chunkData.start;
778
    /** @type {!Object<string, string|number>} */
779
    let item = {
780
      chunkId: subChunk.chunkId,
781
      chunkSize: subChunk.chunkSize,
782
      dwName: this.readUInt32(buffer)
783
    };
784
    if (subChunk.chunkId == "ltxt") {
785
      item.dwSampleLength = this.readUInt32(buffer);
786
      item.dwPurposeID = this.readUInt32(buffer);
787
      item.dwCountry = this.readUInt16_(buffer);
788
      item.dwLanguage = this.readUInt16_(buffer);
789
      item.dwDialect = this.readUInt16_(buffer);
790
      item.dwCodePage = this.readUInt16_(buffer);
791
      item.value = ""; // kept for compatibility
792
    } else {
793
      item.value = this.readZSTR_(buffer, this.head);
794
    }
795
    this.LIST[this.LIST.length - 1].subChunks.push(item);
796
  }
797
798
  /**
799
   * Read the sub chunks of a 'LIST' chunk of type 'INFO'.
800
   * @param {!Uint8Array} buffer The wav file buffer.
801
   * @param {!Object} subChunk The 'LIST' subchunks.
802
   * @private
803
   */
804
  readLISTINFOSubChunks_(buffer, subChunk) {
805
    this.head = subChunk.chunkData.start;
806
    this.LIST[this.LIST.length - 1].subChunks.push({
807
      chunkId: subChunk.chunkId,
808
      chunkSize: subChunk.chunkSize,
809
      value: this.readZSTR_(buffer, this.head)
810
    });
811
  }
812
813
  /**
814
   * Read the 'junk' chunk of a wave file.
815
   * @param {!Uint8Array} buffer The wav file buffer.
816
   * @private
817
   */
818
  readJunkChunk_(buffer) {
819
    /** @type {?Object} */
820
    let chunk = this.findChunk("junk");
821
    if (chunk) {
822
      this.junk = {
823
        chunkId: chunk.chunkId,
824
        chunkSize: chunk.chunkSize,
825
        chunkData: [].slice.call(
826
          buffer.slice(chunk.chunkData.start, chunk.chunkData.end)
827
        )
828
      };
829
    }
830
  }
831
832
  /**
833
   * Read the '_PMX' chunk of a wav file.
834
   * @param {!Uint8Array} buffer The wav file buffer.
835
   * @private
836
   */
837
  read_PMXChunk_(buffer) {
838
    /** @type {?Object} */
839
    let chunk = this.findChunk("_PMX");
840
    if (chunk) {
841
      this.head = chunk.chunkData.start;
842
      this._PMX.chunkId = chunk.chunkId;
843
      this._PMX.chunkSize = chunk.chunkSize;
844
      this._PMX.value = unpackString(
845
        buffer,
846
        this.head,
847
        this.head + this._PMX.chunkSize
848
      );
849
    }
850
  }
851
852
  /**
853
   * Read bytes as a ZSTR string.
854
   * @param {!Uint8Array} bytes The bytes.
855
   * @param {number=} [index=0] the index to start reading.
856
   * @return {string} The string.
857
   * @private
858
   */
859
  readZSTR_(bytes, index = 0) {
860
    for (let i = index; i < bytes.length; i++) {
861
      this.head++;
862
      if (bytes[i] === 0) {
863
        break;
864
      }
865
    }
866
    return unpackString(bytes, index, this.head - 1);
867
  }
868
869
  /**
870
   * Read a number from a chunk.
871
   * @param {!Uint8Array} bytes The chunk bytes.
872
   * @return {number} The number.
873
   * @private
874
   */
875
  readUInt16_(bytes) {
876
    /** @type {number} */
877
    let value = unpack(bytes, this.uInt16, this.head);
878
    this.head += 2;
879
    return value;
880
  }
881
}
882